#pragma once
#include <gflags/gflags.h>
#include <thread>
#include <cctype>
#include<brpc/server.h> 
#include<butil/logging.h>

#include "utils.hpp"// 基础工具接口
#include "data_es.hpp"// es数据管理客户端封装
#include "mysql_user.hpp"// mysql数据管理客户端封装
#include "redis.hpp"// redis数据管理客户端封装
#include "channel.hpp"  // 信道管理模块封装
#include "etcd.hpp"    // 服务注册模块封装   
#include "dms.hpp"// 短信平台SDK模块封装

#include "user.pb.h"  // protobuf框架代码
#include "base.pb.h"
#include "file.pb.h"


namespace zzy_mims 
{
    class UserServiceImpl:public UserService
    {
    public:
        UserServiceImpl(const DMSClient::ptr &dms_client,
        const std::shared_ptr<elasticlient::Client> &es_client,
        const std::shared_ptr<odb::core::database> &mysql_client,
        const std::shared_ptr<sw::redis::Redis> &redis_client,
        const ServChanManager::ptr &channel_manager,
        const std::string &file_service_name) :
        _es_user(std::make_shared<ESUser>(es_client)),
        _mysql_user(std::make_shared<UserTable>(mysql_client)),
        _redis_session(std::make_shared<Session>(redis_client)),
        _redis_status(std::make_shared<Status>(redis_client)),
        _redis_codes(std::make_shared<Code>(redis_client)),
        _file_service_name(file_service_name),
        _mm_channels(channel_manager),
        _dms_client(dms_client){
        _es_user->CreateIndex();
        }
        ~UserServiceImpl(){};
        bool check_nickname(const std::string& nickname)
        {
            return (nickname.size()<=22||nickname.size()>=3);
        }
        bool check_password(const std::string& password)
        {
            if(password.size()>15||password.size()<6)
            return false;
            for(auto e:password)
            if(!std::isalnum(e))
            return false;
            return true;
        }
        //使用用户名和密码注册账号
        virtual void UserRegister(google::protobuf::RpcController* controller,
            const ::zzy_mims::UserRegisterReq* request, 
            ::zzy_mims::UserRegisterRsp* response, 
            ::google::protobuf::Closure* done)
        {    
            //取出数据，检查数据，插入数据，填写回应
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出昵称和密码
            std::string nickname = request->nickname();
            std::string password = request->password();
            //2. 检查昵称是否合法（只能包含字母，数字，下划线_，长度限制 3~22 之间)(x)
            //2. 有汉字，只检查字数限制
            if(!check_nickname(nickname))
            {
                LOG_ERROR("请求{}:用户名<{}>不合法\n",request->request_id(),nickname);
                err_handler(request->request_id(),"用户名不合法");
                return;
            }
            //3. 检查密码是否合法（只能包含字母，数字，长度限制 6~15 之间）
            if(!check_password(password))
            {
                LOG_ERROR("请求{}:密码<{}>不合法\n",request->request_id(),password);
                err_handler(request->request_id(),"密码不合法");
                return;
            } 
            //4. 根据昵称在数据库进行判断是否昵称已存在
            auto user = _mysql_user->select_by_nickname(nickname);
            if(user)
            {
                LOG_ERROR("请求{}:用户名:{}已被占用\n",request->request_id(),nickname);
                err_handler(request->request_id(),"用户名被占用");
                return;
            }
            //5. 向数据库新增数据
            std::string uid = uuid();
            user = std::make_shared<User>(uid,nickname,password);
            bool ret = _mysql_user->insert(user);
            if(ret==false)
            {
                LOG_ERROR("请求{}:向MySQL插入数据失败\n",request->request_id());
                err_handler(request->request_id(),"向MySQL插入数据失败");
                return;
            }
            //6. 向 ES 服务器中新增用户信息
            ret = _es_user->AppendData(uid,"",nickname,"","");
            if(ret==false)
            {
                LOG_ERROR("请求{}:向ES插入数据失败\n",request->request_id());
                err_handler(request->request_id(),"向ES插入数据失败");
                return;
            }
            //7. 组织响应，进行成功与否的响应即可。
            response->set_success(true);
        }
        bool check_phone(const std::string& phone)
        {
            if(phone.size()!=11||phone[0]!='1'||phone[1] < '3'|| phone[1] > '9')
            {
                LOG_ERROR("手机号位数不对,size:{}",phone.size());
                LOG_ERROR("手机号第一位数不对:",phone[0]);
                LOG_ERROR("手机号第二位数不对:",phone[1]);
                return false;
            }   
            for(int i =2;i<11;i++)
            if(phone[i]-'0'>9||phone[i]-'0'<0)
            {
                LOG_ERROR("手机号中存在非数字符号:{}",phone[i]);
                return false;
            }
            return true;  
        }
        void UserLogin(google::protobuf::RpcController* controller,
            const ::zzy_mims::UserLoginReq* request, 
            ::zzy_mims::UserLoginRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 从请求中取出昵称和密码
            std::string nickname = request->nickname();
            std::string password = request->password();
            //2. 通过MySQL判断昵称和密码是否正确
            auto user = _mysql_user->select_by_nickname(nickname);
            if(!user||password!=user->password())
            {
                LOG_ERROR("请求{}:用户名或密码错误{}-{}\n",request->request_id(),nickname,password);
                err_handler(request->request_id(),":用户名或密码错误");
                return;
            }
            //3. 使用redis判断用户是否已经上线
            bool ret = _redis_status->exist(user->user_id());
            if(ret==true)
            {
                LOG_ERROR("请求{}:用户在其他地方已经上线\n",request->request_id());
                err_handler(request->request_id(),"用户在其他地方已经上线");
                return;
            }
            //4. 构建会话，更新登录状态
            std::string sid = uuid();
            _redis_session->append(sid,user->user_id());
            _redis_status->append(user->user_id());
            //5. 构建回应
            response->set_success(true);
            response->set_login_session_id(sid);
        }
        void GetPhoneVerifyCode(google::protobuf::RpcController* controller,
            const ::zzy_mims::PhoneVerifyCodeReq* request, 
            ::zzy_mims::PhoneVerifyCodeRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 取出手机号
            std::string phone = request->phone_number();
            //2. 判断是否合法
            bool ret = check_phone(phone);
            if(ret==false)
            {
                LOG_ERROR("请求{}:手机号错误,phone:{}\n",request->request_id(),phone);
                err_handler(request->request_id(),"手机号错误");
                return;
            }
            //3. 生成四位随机数当验证码
            std::string code = vcode();
            std::string cid =uuid();
            //4. 发送验证码
            ret = _dms_client->send(phone,code);
            if(ret==false)
            {
                LOG_ERROR("请求{}:验证码发送失败,phone:{}\n",request->request_id(),phone);
                err_handler(request->request_id(),"验证码发送失败");
                return;
            }
            //5. 将验证码储存到redis中
            _redis_codes->append(cid,code);
            //6. 构建回应
            response->set_success(true);
            response->set_verify_code_id(cid);
            LOG_INFO("向手机号:{}发送的验证码已经发送成功!\n",phone);
        }
        void PhoneRegister(google::protobuf::RpcController* controller,
            const ::zzy_mims::PhoneRegisterReq* request, 
            ::zzy_mims::PhoneRegisterRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 取出手机号,验证码和验证ID
            std::string phone = request->phone_number();
            std::string code_id = request->verify_code_id();
            std::string code = request->verify_code();
            //2. 检查手机号是否合法
            bool ret = check_phone(phone);
            if(ret==false)
            {
                LOG_ERROR("请求{}:手机号错误,phone:{}\n",request->request_id(),phone);
                err_handler(request->request_id(),"手机号错误");
                return;
            }
            //3. 使用redis进行验证码的验证
            auto vcode = _redis_codes->code(code_id);
            if(vcode!=code)
            {
                LOG_ERROR("请求{}:验证码错误,code:{}\n",request->request_id(),code);
                err_handler(request->request_id(),"验证码错误");
                return;
            }
            //4. 检查手机号是否已经被注册过
            auto user = _mysql_user->select_by_phone(phone);
            if(user)
            {
                LOG_ERROR("请求{}:手机号已经被注册,phone:{}\n",request->request_id(),phone);
                err_handler(request->request_id(),"手机号已经被注册");
                return;
            }
            //5. 注册手机号
            std::string user_id= uuid();
            user = std::make_shared<User>(user_id,phone);
            ret = _mysql_user->insert(user);
            if(ret==false)
            {
                LOG_ERROR("请求{}:通过手机号向数据库注册用户失败,phone:{}\n",request->request_id(),phone);
                err_handler(request->request_id(),"通过手机号向数据库注册用户失败");
                return;
            }
            //6. 向 ES 服务器中新增用户信息
            ret = _es_user->AppendData(user_id,phone,"","","");
            if(ret==false)
            {
                LOG_ERROR("请求{}:向ES插入数据失败\n",request->request_id());
                err_handler(request->request_id(),"向ES插入数据失败");
                return;
            }
            //7. 组织响应，进行成功与否的响应即可。
            response->set_success(true);
        }
        void PhoneLogin(google::protobuf::RpcController* controller,
            const ::zzy_mims::PhoneLoginReq* request, 
            ::zzy_mims::PhoneLoginRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1. 取出手机号,验证码和验证ID
            std::string phone = request->phone_number();
            std::string code_id = request->verify_code_id();
            std::string code = request->verify_code();
            //2. 检查手机号是否合法
            bool ret = check_phone(phone);
            if(ret==false)
            {
                LOG_ERROR("请求{}:手机号错误,phone:{}\n",request->request_id(),phone);
                err_handler(request->request_id(),"手机号错误");
                return;
            }
            //3. 通过MySQL判断用户是否存在
            auto user = _mysql_user->select_by_phone(phone);
            if(!user)
            {
                LOG_ERROR("请求{}:该手机号的用户不存在,phone{}\n",request->request_id(),phone);
                err_handler(request->request_id(),":该手机号的用户不存在");
                return;
            }
            //4. 使用redis进行验证码的验证
            auto vcode = _redis_codes->code(code_id);
            if(vcode!=code)
            {
                LOG_ERROR("请求{}:验证码错误,code:{}\n",request->request_id(),code);
                err_handler(request->request_id(),"验证码错误");
                return;
            }
            //5. 通过redis判断用户是否已经上线
            ret = _redis_status->exist(user->user_id());
            if(ret==true)
            {
                LOG_ERROR("请求{}:用户在其他地方已经上线\n",request->request_id());
                err_handler(request->request_id(),"用户在其他地方已经上线");
                return;
            }
            //6. 构建会话，更新登录状态
            std::string sid = uuid();
            _redis_session->append(sid,user->user_id());
            _redis_status->append(user->user_id());
            //7. 构建回应
            response->set_success(true);
            response->set_login_session_id(sid);
        }
        //从这一步开始，用户登录之后才会进行的操作
        void GetUserInfo(google::protobuf::RpcController* controller,
            const ::zzy_mims::GetUserInfoReq* request, 
            ::zzy_mims::GetUserInfoRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1.取出用户ID
            std::string uid = request->user_id();

            //2.使用MySQL数据库查询用户信息
            auto user = _mysql_user->select_by_id(uid);
            if(!user)
            {
                LOG_ERROR("请求{}:该用户ID的用户不存在,uid:{}\n",request->request_id(),uid);
                err_handler(request->request_id(),":该用户ID的用户不存在");
                return;
            };

            //3.组织用户信息结构
            UserInfo* user_info = response->mutable_user_info();
            user_info->set_user_id(uid);
            user_info->set_nickname(user->nickname());
            user_info->set_description(user->description());
            user_info->set_phone(user->phone());
            if(user->avatar_id().size())
            {
                auto channel = _mm_channels->choose(_file_service_name);
                if(!channel)
                {
                    LOG_ERROR("请求{}:没有找到能提供文件服务的节点,filename:{}\n",request->request_id(),_file_service_name);
                    err_handler(request->request_id(),":没有找到能提供文件服务的节点");
                    return;
                }
                 //进行RPC调用
                ::zzy_mims::FileService_Stub stub(channel.get());
                ::zzy_mims::GetSingleFileReq  req;
                ::zzy_mims::GetSingleFileRsp resp;

                //填写服务请求需要的数据并进行服务调用
                req.set_request_id(request->request_id());
                req.set_file_id(user->avatar_id());

                brpc::Controller cntl;
                stub.GetSingleFile(&cntl,&req,&resp,nullptr);//同步调用
                if(cntl.Failed()==true||resp.success()==false)
                {
                    LOG_ERROR("请求{}:文件子服务调用失败,filename:{},avatar_id_size:{},errtext:{}\n"
                        ,request->request_id(),_file_service_name,user->avatar_id().size(),cntl.ErrorText());
                    err_handler(request->request_id(),":文件子服务调用失败");
                    return;
                }
                user_info->set_avatar(resp.file_data().file_content());
            }

            response->set_success(true);     
            
        }
        void GetMultiUserInfo(google::protobuf::RpcController* controller,
            const ::zzy_mims::GetMultiUserInfoReq* request, 
            ::zzy_mims::GetMultiUserInfoRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1.取出用户ID
            std::vector<std::string> uid_list(request->users_id_size());
            for(int i =0;i<request->users_id_size();i++)
            uid_list[i] = request->users_id(i);

            //2.使用MySQL数据库查询用户信息
            auto users = _mysql_user->select_multi_users(uid_list); 
            if(users.size()!=request->users_id_size())
            {
                LOG_ERROR("请求{}:从数据库查找的用户信息与请求数量不一致,req_size:{},select_size:{}\n"
                    ,request->request_id(),request->users_id_size(),users.size());
                err_handler(request->request_id(),":该用户ID的用户不存在");
                return;
            };

            //3.组织用户信息结构
            for(int i =0;i<users.size();i++)
            {
                UserInfo user_info;
                auto user =  users[i];
                user_info.set_user_id(user.user_id());
                user_info.set_nickname(user.nickname());
                user_info.set_description(user.description());
                user_info.set_phone(user.phone());
                if(user.avatar_id().size())
                {
                    auto channel = _mm_channels->choose(_file_service_name);
                    if(!channel)
                    {
                        LOG_ERROR("请求{}:没有找到能提供文件服务的节点,filename:{}\n",request->request_id(),_file_service_name);
                        err_handler(request->request_id(),":没有找到能提供文件服务的节点");
                        return;
                    }
                        //进行RPC调用
                    ::zzy_mims::FileService_Stub stub(channel.get());
                    ::zzy_mims::GetSingleFileReq  req;
                    ::zzy_mims::GetSingleFileRsp resp;

                    //填写服务请求需要的数据并进行服务调用
                    req.set_request_id(request->request_id());
                    req.set_file_id(user.avatar_id());

                    brpc::Controller cntl;
                    stub.GetSingleFile(&cntl,&req,&resp,nullptr);//同步调用
                    if(cntl.Failed()==true||resp.success()==false)
                    {
                        LOG_ERROR("请求{}:文件子服务调用失败,filename:{},avatar_id_size:{},errtext:{}\n"
                            ,request->request_id(),_file_service_name,user.avatar_id().size(),cntl.ErrorText());
                        err_handler(request->request_id(),":没有找到能提供文件服务的节点");
                        return;
                    }
                    user_info.set_avatar(resp.file_data().file_content());
                }
                response->mutable_users_info()->insert({user.user_id(),user_info});
            }
            response->set_success(true);     

        }
        void SetUserAvatar(google::protobuf::RpcController* controller,
            const ::zzy_mims::SetUserAvatarReq* request, 
            ::zzy_mims::SetUserAvatarRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
             //1.取出用户ID
             std::string uid = request->user_id();

             //2.使用MySQL数据库查询用户信息
             auto user = _mysql_user->select_by_id(uid);
             if(!user)
             {
                 LOG_ERROR("请求{}:该用户ID的用户不存在,uid:{}\n",request->request_id(),uid);
                 err_handler(request->request_id(),":该用户ID的用户不存在");
                 return;
             };
             //3. 上传头像文件到文件服务器中，获取文件ID
             auto channel = _mm_channels->choose(_file_service_name);
            if(!channel)
            {
                LOG_ERROR("请求{}:没有找到能提供文件服务的节点,filename:{}\n",request->request_id(),_file_service_name);
                err_handler(request->request_id(),":没有找到能提供文件服务的节点");
                return;
            }
                //进行RPC调用
            ::zzy_mims::FileService_Stub stub(channel.get());
            ::zzy_mims::PutSingleFileReq  req;
            ::zzy_mims::PutSingleFileRsp resp;

                //填写服务请求需要的数据并进行服务调用
            req.set_request_id(request->request_id());
            req.mutable_file_data()->set_file_name("");
            req.mutable_file_data()->set_file_size(request->avatar().size());
            req.mutable_file_data()->set_file_content(request->avatar());


            brpc::Controller cntl;
            stub.PutSingleFile(&cntl,&req,&resp,nullptr);//同步调用
            if(cntl.Failed()==true||resp.success()==false)
            {
                LOG_ERROR("请求{}:文件子服务调用失败,filename:{},errtext:{}\n"
                    ,request->request_id(),_file_service_name,cntl.ErrorText());
                err_handler(request->request_id(),":没有找到能提供文件服务的节点");
                return;
            }
            //4. 更新MySQL数据库
            user->avatar_id(resp.file_info().file_id());
            bool ret = _mysql_user->update(user);
            if(ret==false)
            {
                LOG_ERROR("请求{}:MySQL数据库更新头像ID失败,avatar_id:{}\n",request->request_id(),resp.file_info().file_id());
                err_handler(request->request_id(),"MySQL数据库更新头像ID失败");
                return;
            }
            //5 向 ES 服务器中更新用户信息
            ret = _es_user->AppendData(user->user_id(),user->phone(),user->nickname(),user->avatar_id(),user->description());
            if(ret==false)
            {
                LOG_ERROR("请求{}:向ES更新头像ID失败,avatar_id:{}\n",request->request_id(),resp.file_info().file_id());
                err_handler(request->request_id(),"向ES更新头像ID失败");
                return;
            }
            //6. 组织响应，进行成功与否的响应即可。
            response->set_success(true);
        }
        void SetUserNickname(google::protobuf::RpcController* controller,
            const ::zzy_mims::SetUserNicknameReq* request, 
            ::zzy_mims::SetUserNicknameRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1.取出用户ID
            std::string uid = request->user_id();

            //2.检查请求的姓名是否合法
            if(!check_nickname(request->nickname()))
            {
                LOG_ERROR("请求{}:用户名<{}>不合法\n",request->request_id(),request->nickname());
                err_handler(request->request_id(),"用户名不合法");
                return;
            }

            //3.使用MySQL数据库查询用户信息
            auto user = _mysql_user->select_by_id(uid);
            if(!user)
            {
                LOG_ERROR("请求{}:该用户ID的用户不存在,uid:{}\n",request->request_id(),uid);
                err_handler(request->request_id(),":该用户ID的用户不存在");
                return;
            };
            //4.更新MySQL数据库
            user->nickname(request->nickname());
            bool ret = _mysql_user->update(user);
            if(ret==false)
            {
                LOG_ERROR("请求{}:MySQL数据库更新用户昵称失败,nickname:{}\n",request->request_id(),request->nickname());
                err_handler(request->request_id(),"MySQL数据库更新用户昵称失败");
                return;
            }
            //5 向 ES 服务器中更新用户信息
            ret = _es_user->AppendData(user->user_id(),user->phone(),user->nickname(),user->avatar_id(),user->description());
            if(ret==false)
            {
                LOG_ERROR("请求{}:ES数据库更新用户昵称失败,nickname:{}\n",request->request_id(),request->nickname());
                err_handler(request->request_id(),"ES数据库更新用户昵称失败");
                return;
            }
            //6. 组织响应，进行成功与否的响应即可。
            response->set_success(true);   
        }
        void SetUserDescription(google::protobuf::RpcController* controller,
            const ::zzy_mims::SetUserDescriptionReq* request, 
            ::zzy_mims::SetUserDescriptionRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1.取出用户ID
            std::string uid = request->user_id();


            //2.使用MySQL数据库查询用户信息
            auto user = _mysql_user->select_by_id(uid);
            if(!user)
            {
                LOG_ERROR("请求{}:该用户ID的用户不存在,uid:{}\n",request->request_id(),uid);
                err_handler(request->request_id(),":该用户ID的用户不存在");
                return;
            };
            //3.更新MySQL数据库
            user->description(request->description());
            bool ret = _mysql_user->update(user);
            if(ret==false)
            {
                LOG_ERROR("请求{}:MySQL数据库更新用户签名失败,description:{}\n",request->request_id(),request->description());
                err_handler(request->request_id(),"MySQL数据库更新用户签名失败");
                return;
            }
            //4 向 ES 服务器中更新用户信息
            ret = _es_user->AppendData(user->user_id(),user->phone(),user->nickname(),user->avatar_id(),user->description());
            if(ret==false)
            {
                LOG_ERROR("请求{}:MySQL数据库更新用户签名失败,description:{}\n",request->request_id(),request->description());
                err_handler(request->request_id(),"MySQL数据库更新用户签名失败");
                return;;
            }
            //5. 组织响应，进行成功与否的响应即可。
            response->set_success(true);
        }
        void SetUserPhoneNumber(google::protobuf::RpcController* controller,
            const ::zzy_mims::SetUserPhoneNumberReq* request, 
            ::zzy_mims::SetUserPhoneNumberRsp* response, 
            ::google::protobuf::Closure* done)
        {
            brpc::ClosureGuard rpc_guard(done);//和RAII思想类似，在局部域结束后自动调用done->Run();
            response->set_request_id(request->request_id());
            auto err_handler = [this,response](const std::string& rid,const std::string& errmsg)
            {
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            //1.取出用户ID,code,code_id
            std::string uid = request->user_id();
            std::string phone = request->phone_number();
            std::string code_id = request->phone_verify_code_id();
            std::string code = request->phone_verify_code(); 

            //2.检查请求的手机号是否合法
            if(!check_phone(phone))
            {
                LOG_ERROR("请求{}:手机号<{}>不合法\n",request->request_id(),phone);
                err_handler(request->request_id(),"手机号不合法");
                return;
            }
            //3. 使用redis进行验证码的验证
            auto vcode = _redis_codes->code(code_id);
            if(vcode!=code)
            {
                LOG_ERROR("请求{}:验证码错误,code:{}\n",request->request_id(),code);
                err_handler(request->request_id(),"验证码错误");
                return;
            }
            // //4. 检查手机号是否已经被注册过
            auto user = _mysql_user->select_by_phone(phone);
            if(user)
            {
                LOG_ERROR("请求{}:该手机号已经被注册,phone:{}\n",request->request_id(),phone);
                err_handler(request->request_id(),"该手机号已经被注册");
                return;
            }  
            //5.使用MySQL数据库查询用户信息
            user = _mysql_user->select_by_id(uid);
            if(!user)
            {
                LOG_ERROR("请求{}:该用户ID的用户不存在,uid:{}\n",request->request_id(),uid);
                err_handler(request->request_id(),":该用户ID的用户不存在");
                return;
            };
            //6.更新MySQL数据库
            user->phone(phone);
            bool ret = _mysql_user->update(user);
            if(ret==false)
            {
                LOG_ERROR("请求{}:MySQL数据库更新用户手机号失败,phone:{}\n",request->request_id(),phone);
                err_handler(request->request_id(),"MySQL数据库更新用户手机号失败");
                return;
            }
            //7 向 ES 服务器中更新用户信息
            ret = _es_user->AppendData(user->user_id(),user->phone(),user->nickname(),user->avatar_id(),user->description());
            if(ret==false)
            {
                LOG_ERROR("请求{}:ES数据库更新用户昵称失败,nickname:{}\n",request->request_id(),user->nickname());
                err_handler(request->request_id(),"ES数据库更新用户昵称失败");
                return;
            }
            //8. 组织响应，进行成功与否的响应即可。
            response->set_success(true);  
            
        }
    private:
        //服务操作句柄
        ESUser::ptr _es_user;
        UserTable::ptr _mysql_user;
        Session::ptr _redis_session;
        Status::ptr _redis_status;
        Code::ptr _redis_codes;
        DMSClient::ptr _dms_client;
        ServChanManager::ptr _mm_channels;

        std::string _file_service_name;
    };
    class UserServer
    {
    public:
        using ptr = std::shared_ptr<UserServer>;
        UserServer(const Discovery::ptr& service_discoverer,const std::shared_ptr<brpc::Server>& rpc_server)
        :_service_discoverer(service_discoverer),_rpc_server(rpc_server){};
        ~UserServer(){};
        void start()
        {
            _rpc_server->RunUntilAskedToQuit();
        }
    private:
        Discovery::ptr _service_discoverer;
        std::shared_ptr<brpc::Server> _rpc_server;
    };
    class UserServerBuilder
    {
    public:
        
        void make_es_object(const std::vector<std::string> host_list)
        {
            _es_client=ESClientFactory::create(host_list);
        }
        void make_mysql_object 
            (const std::string& user,
            const std::string& password,
            const std::string& db,
            const std::string& host,
            const std::string& charset,
            const int port,const int conn_max_pool)
        {
            _mysql_client = ODBFactory::create(user,password,db,host,charset,port,conn_max_pool);
        }
        void make_redis_object(const std::string& host,const int port,const int db,bool keep_alive)
        {
            _redis_client = RedisClientFactory::create(host,port,db,keep_alive);
        }
        void make_discover_object(const std::string& etcd_host,
        const std::string& base_service_name,const std::string& file_service_name)
        {
            _file_service_name = file_service_name;
            _mm_channels = std::make_shared<ServChanManager>();
            _mm_channels->Declared(file_service_name);
            auto online = bind(&zzy_mims::ServChanManager::ServiceOnline,_mm_channels.get(),std::placeholders::_1,std::placeholders::_2);
            auto offline = bind(&zzy_mims::ServChanManager::ServiceOffline,_mm_channels.get(),std::placeholders::_1,std::placeholders::_2);
            _service_discoverer = std::make_shared<zzy_mims::Discovery>(etcd_host,base_service_name,online,offline);
        }

        void make_reg_object(const std::string& reg_host,
            const std::string& service_name,
            const std::string& access_host)
        {
            _reg_client = std::make_shared<Registery>(reg_host);
            _reg_client->Register(service_name,access_host);
        }
        void make_dms_object(const std::string &access_key_id,const std::string &access_key_secret)
        {
            _dms_client = std::make_shared<DMSClient>(access_key_id,access_key_secret);
        }

        void make_rpc_server(uint16_t port, int32_t timeout, uint8_t num_threads)
        {
            if(!_es_client)
            {
                LOG_ERROR("构造rpc服务时,还未初始化ES客户端\n");
                abort();
            }
            if(!_mysql_client)
            {
                LOG_ERROR("构造rpc服务时,还未初始化MYSQL客户端\n");
                abort();
            }
            if(!_redis_client)
            {
                LOG_ERROR("构造rpc服务时,还未初始化Redis客户端\n");
                abort();
            }
            if(!_dms_client)
            {
                LOG_ERROR("构造rpc服务时,还未初始化dms客户端\n");
                abort();
            }
            _rpc_server = std::make_shared<brpc::Server>();
            UserServiceImpl* user_service = new UserServiceImpl(_dms_client, _es_client,
                _mysql_client, _redis_client, _mm_channels, _file_service_name);
            int ret = _rpc_server->AddService(user_service,brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
            if(ret==-1)
            {
                LOG_ERROR("添加RPC服务失败!");
                abort();
            }
            brpc::ServerOptions options;
            options.idle_timeout_sec=timeout;
            options.num_threads = num_threads;
            ret = _rpc_server->Start(port,&options);
            if(ret==-1)
            {
                LOG_ERROR("speech_RPC服务器启动失败");
                abort();
            }
        }
        UserServer::ptr build()
        {
            if(!_rpc_server)
            {
                LOG_ERROR("构造UserServer时,还未初始化rpc客户端\n");
                abort();
            }
            if(!_service_discoverer)
            {
                LOG_ERROR("构造UserServer时,还未初始化discover客户端\n");
                abort();
            }
            if(!_reg_client)
            {
                LOG_ERROR("构造UserServer时,还未初始化registry客户端\n");
                abort();
            }
            UserServer::ptr server = std::make_shared<UserServer>(_service_discoverer,_rpc_server);
            return server;
        }



    private:
    //用户信息管理
        std::shared_ptr<elasticlient::Client> _es_client;
        std::shared_ptr<odb::core::database> _mysql_client;
        std::shared_ptr<sw::redis::Redis> _redis_client;

    //服务管理
        std::string _file_service_name;
        ServChanManager::ptr _mm_channels;
        
        Discovery::ptr _service_discoverer;
        Registery::ptr _reg_client;

    //短信API调用客户端
        std::shared_ptr<DMSClient> _dms_client;
    
        std::shared_ptr<brpc::Server> _rpc_server;
    };
}
